package com.sias.spring;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 吴文杰
 * @version 1.0
 * @createTime 2023-05-28 11:20:27
 */
@SuppressWarnings("all")
public class ApplicationContext {
  private Class configClass;

  private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

  private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

  private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

  public ApplicationContext(Class configClass) {
    this.configClass = configClass;
    if (configClass.isAnnotationPresent(ComponentScan.class)) {
      ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
      String path = componentScan.value(); // 扫描路径  com.sias.spring.service

      path = path.replace(".", "/");
      ClassLoader classLoader = ApplicationContext.class.getClassLoader();
      URL resource = classLoader.getResource(path);
      File file = new File(resource.getFile());
      if (file.isDirectory()) {
        File[] files = file.listFiles();
        for (File f : files) {
          String fileName = f.getAbsolutePath();

          if (fileName.endsWith(".class")) {
            String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));

            className = className.replace("\\", ".");
            Class<?> aClass = null;
            try {
              aClass = classLoader.loadClass(className);
              if (aClass.isAnnotationPresent(Component.class)) {

                if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                  BeanPostProcessor instance = (BeanPostProcessor) aClass.newInstance();
                  beanPostProcessors.add(instance);
                }

                Component component = aClass.getAnnotation(Component.class);
                String beanName = component.value();


                if (beanName.equals("")) {
                  beanName = Introspector.decapitalize(aClass.getSimpleName());
                }

                BeanDefinition beanDefinition = new BeanDefinition();
                beanDefinition.setType(aClass);
                if (aClass.isAnnotationPresent(Scope.class)) {
                  Scope scope = aClass.getAnnotation(Scope.class);
                  beanDefinition.setScope(scope.value());
                } else {
                  beanDefinition.setScope("singleton");
                }
                beanDefinitionMap.put(beanName, beanDefinition);
              }
            } catch (ClassNotFoundException e) {
              e.printStackTrace();
            } catch (InstantiationException e) {
              e.printStackTrace();
            } catch (IllegalAccessException e) {
              e.printStackTrace();
            }
          }
        }
      }
      for (String beanName : beanDefinitionMap.keySet()) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition.getScope().equals("singleton")) {
          Object bean = createBean(beanName, beanDefinition);
          singletonObjects.put(beanName, bean);
        }
      }
    }
  }


  private Object createBean(String name, BeanDefinition beanDefinition) {
    Class type = beanDefinition.getType();
    try {
      Object instance = type.getConstructor().newInstance();

      // 依赖注入
      for (Field f : type.getDeclaredFields()) {
        if (f.isAnnotationPresent(Autowired.class)) {
          f.setAccessible(true);
          f.set(instance, getBean(f.getName()));
        }
      }

      // Aware
      if (instance instanceof BeanNameAware) {
        ((BeanNameAware) instance).setBeanName(name);
      }
      // BeanPostProcesser 初始化后  AOP
      // BeanPostProcesser 初始化前
      for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
        instance = beanPostProcessor.postProcessBeforeInitialization(name, instance);
      }

      // 初始化
      if (instance instanceof InitialzingBean) {
        ((InitialzingBean) instance).afterPropertiesSet();
      }

      // BeanPostProcesser 初始化后
      for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
        instance = beanPostProcessor.postProcessAfterInitialization(name, instance);
      }

      return instance;

    } catch (InstantiationException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (InvocationTargetException e) {
      e.printStackTrace();
    } catch (NoSuchMethodException e) {
      e.printStackTrace();
    }

    return null;
  }

  public Object getBean(String beanName) {
    BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
    if (beanDefinition == null) {
      throw new NullPointerException();
    } else {
      String scope = beanDefinition.getScope();
      if (scope.equals("singleton")) {
        Object bean = singletonObjects.get(beanName);
        if (bean == null) {
          Object bean1 = createBean(beanName, beanDefinition);
          singletonObjects.put(beanName, bean1);
          return bean1;
        }
        return bean;
      } else {
        return createBean(beanName, beanDefinition);
      }
    }
  }

}
